import backtrader as bt

class StrategyClass(bt.Strategy):
    '''七星落长空'''
    params = dict(
        version = 'Y',     # 'Y': 阴阳版、'Z':涨跌版
        stop4low = False,  # 高位回落止盈参照最低价还是收盘价
        risestop1 = 1.1,   # 高位回落10%止盈
        risestop2 = 1.15,  # 计划止盈
        fallstop = 0.92,   # 止损
        afterdown = True,  # 是否检测下降通道
        maperiod = 22,     # 均线判断下降
        hiperiod = 100,    # 下降检测的高点范围
        downrate = 0.7,    # 均线低于高点的70% 即下降了 30%
        code = None,
        name = None,
        log = True,
    )

    def log(self, txt, dt=None, force=False):
        if force or self.p.log:
            dt = dt or self.data.datetime.datetime()
            who = f'{self.p.code} {self.p.name} ' if self.p.code else ''
            print(f'{dt.isoformat()} {who}{txt}')

    def notify_order(self, order):
        if order.status in [bt.Order.Submitted, bt.Order.Accepted]:
            return

        if order.status == order.Completed:
            if order.isbuy():
                buytxt = '买入价:%.2f, 量:%s, 持仓:%s' % (order.executed.price, order.executed.size, self.position.size)
                self.log(buytxt, bt.num2date(order.executed.dt))
            else:
                selltxt = '卖出价, %.2f, 量:%s, 持仓:%s' % (
                    order.executed.price, order.executed.size, self.position.size)
                if 'log' in order.info:
                    selltxt = '%s %s' % (selltxt, order.info.log)
                self.log(selltxt, bt.num2date(order.executed.dt))

        elif order.status in [order.Expired, order.Canceled, order.Margin]:
            if 'log' not in order.info:
                self.log('%s , %s' % (order.Status[order.status], order))
        pass

    def __init__(self):
        z = self.data.close - self.data.close(-1)
        y = self.data.close - self.data.open
        x = y if self.p.version == 'Y' else z
        if self.p.afterdown:
            hi100 = bt.ind.Highest(self.data.high, period=self.p.hiperiod)
            ema22 = bt.ind.EMA(self.data, period=self.p.maperiod)
            down = hi100 * self.p.downrate > ema22
            self.signal = bt.And(x(-6) < 0, x(-5) < 0, x(-4) < 0, x(-3) > 0, x(-2) > 0, x(-1) < 0, x(0) > 0, down)
        else:
            self.signal = bt.And(x(-6) < 0, x(-5) < 0, x(-4) < 0, x(-3) > 0, x(-2) > 0, x(-1) < 0, x(0) > 0)

        self.hab = 0
        self.order = None

    def next(self):
        if self.order:
            if self.data.high[0] > self.hab:
                self.hab = self.data.high[0]
            o = self.order
            if o.status in [o.Canceled, o.Completed]:
                self.order = None
                self.hab = 0
            elif o.status in [o.Expired, o.Margin]:
                print('订单状态异常', o.status)
            elif (self.data.low[0] if self.p.stop4low else self.data.close[0]) * self.p.risestop1 < self.hab:
                self.sell(exectype=bt.Order.Market, log=' (高位回落)止盈 ', oco=o)
        elif self.signal:
            self.buy(exectype=bt.Order.Market, log='七星落长空')
            price = self.data.close[0]
            o = self.sell(exectype=bt.Order.Limit, price=price * self.p.risestop2, log='按计划止盈')
            self.sell(exectype=bt.Order.Stop, price=price * self.p.fallstop, log="初始止损", oco=o)
            self.order = o

SevenStar = StrategyClass
